#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/wait.h>
#include <unistd.h>
#include <signal.h>

#include "config.h"
#include "performConnection.h"
#include "shared_memory.h"
#include "socket.h"
#include "think.h"

/* TODO: Clean-Up implementieren */
void Aufraeumen(void) {
       
}


void Hilfe_ausgeben(void) {
        fprintf(stdout, "Nutzung:\n");
        fprintf(stdout, "Benoetigtes Argument:\n");
        fprintf(stdout, "   -i <Game-ID> : <Game-ID> ist die elfstellige ID eines existierenden Spiels.\n");
        fprintf(stdout, "Optionales Argument:\n");
        fprintf(stdout, "   -c <Konfigurationsdatei> : <Konfigurationsdatei> ist eine Konfigurationsdatei im selben Verzeichnis.\n");
        fprintf(stdout, "Falls '-c' nicht verwendet wird, so wird eine Standardkonfiguration verwendet.\n");
}

/* Signal Handler */
void Handler(int Signal){
        switch(Signal){
          case SIGUSR1:
            think(Info_struct->shm_ID2);
        default:
            break;
        }
}



int main(int argc, char *argv[]) {
        char Spiel_ID[11];
        int fd[2];
        int getopt_Wert = 0;
        int Spiel_ID_angegeben = 0;
        int Konf_angegeben = 0;

        fd[0] = fd[1] = 0;


        /* Für fork */
        int status;                             // Wird benötigt um Zombieprozesse auszuschließen
        pid_t  pid;

        /*
         * Hier werden die Parameter, die an main() übergeben wurden, verarbeitet. Es wird eine elfstellige Game-ID erwartet und optional
         * kann eine Konfigurationsdatei angegeben werden.
         */

        while ((getopt_Wert = getopt(argc, argv, ":i:c:")) != -1) {
                switch (getopt_Wert) {
                        case 'i': // Game-ID
                                Spiel_ID_angegeben = 1;
                                if (strlen(optarg) == 11) { // Prüfen ob Eingabe eine valide Game-ID ist.
                                strcpy(Spiel_ID, optarg);
                                }
                                else {
                                        fprintf(stderr, "Fehler beim Aufruf, uebergeben sie als Argument nur eine einzelne 11-stellige Game-ID.\n");
                                        return EXIT_FAILURE;
                                }
                                break;

                        case 'c': // Konfigurationsdatei
                                Konf_angegeben = 1;
                                Konfig_lesen(optarg);
                                break;

                        default:
                                Hilfe_ausgeben();
                                return EXIT_FAILURE;
                }
        }

        if (Spiel_ID_angegeben == 0) {
                fprintf(stderr, "Fehlender Parameter: Game-ID.\n");
                Hilfe_ausgeben();
                return EXIT_FAILURE;
        }

        if (Konf_angegeben == 0) {
                Konfig_lesen("client.conf");
                fprintf(stdout, "Standardkonfiguration wird verwendet.\n");
        }


        /* Erzeugen des Shared-Memory-Segments, das generelle Informationen enthält. */

        int shm_ID = shm_Segment_anlegen(sizeof(Info));
        if (shm_ID == -1) {
                Aufraeumen();
                return EXIT_FAILURE;
        }

        void *Info_Zeiger = shm_Segment_anbinden(shm_ID);
        if (Info_Zeiger == (void *) -1) {
                Aufraeumen();
                return EXIT_FAILURE;
        }

        Info_struct = Info_Zeiger; // Struct im erzeugten shm-Segment ablegen.



        /* Erzeugen des Shared-Memory-Segments, das Spieler-Informationen enthält. */
            Info_struct->shm_ID = shm_Segment_anlegen(2*sizeof(Spieler));
            if (Info_struct->shm_ID == -1) {
                Aufraeumen();
                return EXIT_FAILURE;
            }

            Spieler *Spieler_struct = shm_Segment_anbinden(Info_struct->shm_ID);
            if (Spieler_struct == (void *) -1) {
                Aufraeumen();
                return EXIT_FAILURE;
            }


        Info_struct->shm_ID2 = shm_Segment_anlegen(sizeof(SP)*18);
            if (Info_struct->shm_ID2 == -1) {
                Aufraeumen();
                return EXIT_FAILURE;
            }


        /* Pipe erzeugen. */

        if (pipe (fd) < 0) {
        perror ("Fehler beim Einrichten der Pipe.");
        return EXIT_FAILURE;
    }
        Info_struct->fd0 = fd[0];
        Info_struct->fd1 = fd[1];

        /* Erzeugen zweier separater Prozesse, dem Connector und dem Thinker. */

        pid = fork();

        if (signal(SIGUSR1,Handler)==SIG_ERR){
                perror("Fehler beim Signalhandler");
        }


        if (pid == 0) {
                /* Kindprozess (Connector).
                 * wenn fork eine 0 zurückgibt, befinden wir uns im Kindprozess.
                 */

                /* Schreibseite der Pipe schließen.*/
                close(fd[1]);

                int sock = Verbindung_herstellen();
                performConnection(sock, Spiel_ID);

                Spielphase_durchfuehren(sock,ppid);
                Verbindung_beenden();
        }
        else if (pid > 0) {
                /* Elternprozess (Thinker).
                 * Gibt fork einen Wert größer 0 zurück, befinden wir uns im Elternprozess.
                 */

                /* Leseseite der Pipe schließen.*/
                close(fd[0]);

                /* Verhindern, dass Prozess zum Zombie wird */
                pid = wait(&status);
        }
        else {
                fprintf (stderr, "Fehler bei der Prozesserzeugung.");
                return EXIT_FAILURE;
        }

        return EXIT_SUCCESS;
}

